//
// Created by 马皓然 on 2025/10/28.
//
#include "can_bsp.h"
#include <string.h>
#include "main.h"
#include <stdio.h>
/* Global Variables ----------------------------------------------------------*/
QueueHandle_t xCanRxQueue = NULL;


/* Functions -----------------------------------------------------------------*/

/**
 * @brief  初始化CAN总线（包括HAL启动）和CAN报文过滤器。
 *
 * @param  hcan 指向CAN句柄的指针（通常是CubeMX生成的&hcan1）。
 *
 * @return HAL状态（HAL_OK if successful）
 *
 * @note   此函数是CAN外设初始化流程的一部分，在调用HAL_CAN_Start之前，
 * 必须先创建接收队列。
 * @author stm32小高手
 * @date   2025-10-28
 */
HAL_StatusTypeDef CAN_Filter_Init(CAN_HandleTypeDef *hcan)
{
    // 1. 创建CAN接收队列
    xCanRxQueue = xQueueCreate(CAN_RX_QUEUE_SIZE, sizeof(CAN_RxFrame_t));
    if (xCanRxQueue == NULL)
    {
        // 队列创建失败，系统可能无法正常工作
        return HAL_ERROR;
    }

    // 2. 配置CAN过滤器
    CAN_FilterTypeDef  sFilterConfig;

    sFilterConfig.FilterBank = 0;                     // 过滤器组0
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩码模式
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;

    // 设置为接收所有ID：ID = 0, Mask = 0
    // 这样，只要是有效的CAN报文都会被接收
    sFilterConfig.FilterIdHigh = 0x0000;
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;
    sFilterConfig.FilterMaskIdLow = 0x0000;

    sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器结果送入FIFO0
    sFilterConfig.FilterActivation = ENABLE;
    sFilterConfig.SlaveStartFilterBank = 14;          // 仅用于双CAN配置

    if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK)
    {
        return HAL_ERROR;
    }

    // 3. 启动CAN总线
    if (HAL_CAN_Start(hcan) != HAL_OK)
    {
        return HAL_ERROR;
    }

    // 4. 激活CAN接收中断（此后，接收到的报文会触发中断回调）
    if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
 * @brief  CAN报文发送函数封装。
 *
 * @param  pHeader 指向CAN发送报文头的指针。
 * @param  aData 指向要发送的8字节数据的指针。
 *
 * @return HAL状态（HAL_OK if successful）
 *
 * @note   此函数会阻塞等待一个空闲的发送邮箱，直到发送成功或出错。
 * @author stm32小高手
 * @date   2025-10-28
 */
HAL_StatusTypeDef CAN_Send_Message(CAN_TxHeaderTypeDef *pHeader, uint8_t aData[])
{
    uint32_t TxMailbox;
    // 确保CAN发送邮箱非满，最多等待10ms
    if (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) == 0)
    {
        // 无空闲邮箱，发送失败
        return HAL_ERROR;
    }

    // 发送报文
    if (HAL_CAN_AddTxMessage(&hcan1, pHeader, aData, &TxMailbox) != HAL_OK)
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* --------------------------- 中断服务函数封装 ------------------------------- */

/**
 * @brief  CAN RX FIFO0 挂起消息中断回调函数。
 * @note   此函数是HAL库的弱定义函数，当CAN FIFO0收到消息时，会被CAN中断调用。
 * 我们在这里重写它，实现中断到任务的非阻塞数据传递。
 *
 * @param  hcan 指向CAN句柄的指针。
 *
 * @return None
 *
 * @note   **强制要求：** 在中断中必须使用ISR安全函数(`xQueueSendFromISR`)，
 * 避免任何阻塞操作，并保持执行时间最短。
 * @see    xQueueSendFromISR
 * @author stm32小高手
 * @date   2025-10-28
 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    CAN_RxFrame_t rx_frame;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    // 1. 从CAN FIFO0 读取报文
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_frame.rx_header, rx_frame.rx_data) == HAL_OK)
    {
        // 2. 将报文发送到FreeRTOS队列
        // 注意：xQueueSendFromISR是ISR安全的，它是将数据从中断传递给任务的规范方法。
        if (xQueueSendFromISR(xCanRxQueue, &rx_frame, &xHigherPriorityTaskWoken) != pdPASS)
        {
            // 队列已满，消息丢失。这表明协议解析任务处理速度不够快，需要优化任务优先级或队列大小。
        }
    }

    // 3. 上下文切换请求
    // 如果xHigherPriorityTaskWoken被置为pdTRUE，说明有一个更高优先级的任务因接收到此数据而被唤醒，
    // 需要立即进行任务切换。
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}